/*
 * UserGuard.java
 *
 * Created on July 1, 2007, 4:24 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.auth.service.app;

import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import org.atomojo.auth.service.db.AuthDB;
import org.atomojo.auth.service.db.Group;
import org.atomojo.auth.service.db.Permission;
import org.atomojo.auth.service.db.Realm;
import org.atomojo.auth.service.db.RealmUser;
import org.atomojo.auth.service.db.User;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.ChallengeScheme;
import org.restlet.routing.Filter;
import org.restlet.security.ChallengeAuthenticator;
import org.restlet.security.Verifier;

/**
 *
 * @author alex
 */
public class RealmUserGuard extends ChallengeAuthenticator
{

   protected ChallengeScheme myScheme;
   protected AuthDB db;
   protected Realm realm;
   protected Set<Permission> permissions;
   protected Group group;
   protected Permission adminPermission;

   /** Creates a new instance of UserGuard */
   public RealmUserGuard(Context context,AuthDB db,Realm realm,ChallengeScheme scheme,String realmName,Permission adminPermission)
   {
      super(context,scheme,realmName);
      this.myScheme = scheme;
      this.db = db;
      this.realm = realm;
      this.permissions = null;
      this.group = null;
      this.adminPermission = adminPermission;
      setVerifier(new Verifier() {
         public int verify(Request request,Response response) {
            ChallengeResponse cr = request.getChallengeResponse();
            if (cr==null) {
               return Verifier.RESULT_MISSING;
            }
            String identifier = request.getChallengeResponse().getIdentifier();
            char[] secret = request.getChallengeResponse().getSecret();
            if ((identifier != null) && (secret != null)) {
               getContext().getLogger().info("Authenticating " + identifier);
               try {
                  Realm currentRealm = RealmUserGuard.this.realm == null ? (Realm) request.getAttributes().get(AuthApplication.REALM_ATTR) : RealmUserGuard.this.realm;
                  if (currentRealm != null) {
                     RealmUser user = AuthResource.findRealmUser(RealmUserGuard.this.db, currentRealm, identifier);
                     if (user != null && user.getUser().checkPassword(new String(secret))) {
                        getContext().getLogger().info("Authenticated: " + user.getAlias() + ", checking roles and groups");
                        if (permissions != null) {
                           for (Permission p : permissions) {
                              if (!user.hasPermission(p)) {
                                 user = null;
                                 break;
                              }
                           }
                           if (user != null && group != null) {
                              if (!user.isMemberOf(group)) {
                                 user = null;
                              }
                           }
                        }
                        if (user != null && !hasRealmSpecific(request, user)) {
                           user = null;
                        }
                        if (user != null) {
                           getContext().getLogger().info("Accepted: " + user.getAlias());
                        }
                        if (user != null) {
                           if (user != null) {
                              request.getAttributes().put(AuthApplication.USER_ATTR, user);
                           }
                           return Verifier.RESULT_VALID;
                        }
                     }
                  }
                  // lookup for admin
                  User adminUser = AuthResource.findUser(RealmUserGuard.this.db, identifier);
                  if (adminUser != null && adminUser.hasPermission(RealmUserGuard.this.adminPermission) && adminUser.checkPassword(new String(secret))) {
                     // we have an admin user so shortcut and return 1
                     request.getAttributes().put(AuthApplication.USER_ATTR, adminUser);
                     return Verifier.RESULT_VALID;
                  }
               } catch (SQLException ex) {
                  getContext().getLogger().log(Level.SEVERE, "Cannot check authentication.", ex);
               } catch (NoSuchAlgorithmException ex) {
                  getContext().getLogger().log(Level.SEVERE, "Cannot check authentication.", ex);
               }
            }
            return Verifier.RESULT_INVALID;

         }
      });
   }

   public void addPermission(Permission p)
   {
      if (this.permissions==null) {
         this.permissions = new TreeSet<Permission>();
      }
      this.permissions.add(p);
   }

   public void setGroup(Group group)
   {
      this.group = group;
   }

   public int beforeHandle(Request request, Response response) {
       if (request.getResourceRef().getRemainingPart().startsWith("/auth")) {
          getContext().getLogger().fine("Passing auth request through...");
          return Filter.CONTINUE;
       } else {
          return super.beforeHandle(request, response);
       }
   }

   protected boolean hasRealmSpecific(Request request,RealmUser user) {
      return true;
   }
}
